package tp.badugi.bot;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import com.sun.jmx.remote.util.OrderClassLoaders;
import com.sun.org.apache.xpath.internal.axes.IteratorPool;

public class BotCardInformationProvider {
	
	private char[] cardValues = {'0','K','Q','J','T','9','8','7','6','5','4','3','2','A'};
	
	private final String ATTRIBUTE_COLOR = "color";
	private final String ATTRIBUTE_VALUE = "value";
	
	protected BotCardInformationProvider(){}
	
	protected int getValueOfCardsInHand(String[] cards){
		
		int value = 0;
		
		int[] valuableCards = this.getValuesOfValuableHandCard(cards);
		
		value = this.valuateHandFunction(valuableCards);
		
		return value;
	}
	
	protected int[] getValuesOfValuableHandCard(String cards[]){
		
		int[] result = new int[]{};
		
		List<String> cardList = new ArrayList<String>(Arrays.asList(cards));
		Set<String> intersection;
		
		String[][] colorDup = this.getDuplicateCardByAttribute(cards, this.ATTRIBUTE_COLOR);
		String[][] valueDup = this.getDuplicateCardByAttribute(cards, this.ATTRIBUTE_VALUE);
		
		String commonCard = "";
		
		String[] worstCardsInOrder;
		String[] bestCardsInOrder;
		
		//no duplicates
		//result = all cards
		if( colorDup[0].length == 0 && valueDup[0].length == 0){
			
			result = this.valuateEachCardInSet(cards);
		}
		
		//only color dup so eliminate WORST cards from color dup sets
		//result = ALL CARDs - [n-1 MINIMAL values from color dup sets] (n = color_dup_set_size)
		else if( colorDup[0].length > 0 && valueDup[0].length == 0){
			
			for(int i = 0; i < colorDup.length; i++){
				worstCardsInOrder = this.getCardsWithMinimalValue(colorDup[i], colorDup[i].length-1);
				cardList.removeAll(Arrays.asList(worstCardsInOrder));
			}
			
			result = this.valuateEachCardInSet(cardList.toArray(new String[cardList.size()]));
		}
		
		//only value dup so eliminate ANY cards from value dup sets
		//result = ALL CARDs - [n-1 ANY values from value dup sets] (n = value_dup_set_size)
		else if( colorDup[0].length == 0 && valueDup[0].length > 0){
			
			for(int i = 0; i < valueDup.length; i++){
				worstCardsInOrder = this.getCardsWithMinimalValue(valueDup[i], valueDup[i].length-1);
				cardList.removeAll(Arrays.asList(worstCardsInOrder));
			}
			
			result = this.valuateEachCardInSet(cardList.toArray(new String[cardList.size()]));
			
		}
		
		//for mixed duplicates ie. cards with color and value duplicates
		else if( colorDup[0].length > 0 && valueDup[0].length > 0 ){
			
			intersection = this.getIntersection(colorDup[0], valueDup[0]);
			
			if(intersection.size() != 0){
				commonCard = intersection.iterator().next();
			}
			
			//the same rule as in value_dup.lenght > 0 AND color_dup.lenght == 0 (above)
			//result = any of (value_dup_set_size - 1) elements
			if( colorDup.length == 1 && !commonCard.isEmpty()){
				
				for(int i = 0; i < valueDup.length; i++){
					worstCardsInOrder = this.getCardsWithMinimalValue(valueDup[i], valueDup[i].length-1);
					cardList.removeAll(Arrays.asList(worstCardsInOrder));
				}
				
				result = this.valuateEachCardInSet(cardList.toArray(new String[cardList.size()]));
			}
			
			//separated two el. color and value dup
			//result = BEST card from color dup and ANY card from value dup 
			else if( 	((colorDup.length == 1 && valueDup.length == 1) &&
						(colorDup[0].length == 2 && valueDup[0].length == 2)) 
						&& commonCard.isEmpty()){
				
				worstCardsInOrder = this.getCardsWithMinimalValue(colorDup[0], colorDup[0].length);
				bestCardsInOrder = this.getReversedCardsOrder(worstCardsInOrder);
				
				result = this.valuateEachCardInSet(new String[]{bestCardsInOrder[0], valueDup[0][0]});
			}
			
			//one triple color duplicate and one double value duplicate
			// result = all - common dup - minimal color dup
			else if( colorDup[0].length == 3 && valueDup.length == 1){
				
				cardList.remove(commonCard);
				cardList.remove(this.getCardWithMinimalValue(colorDup[0]));
				
				result = this.valuateEachCardInSet(cardList.toArray(new String[cardList.size()]));
			}
			
			//two double color duplicate and one double value duplicate
			// result = two best cards from all
			else if( colorDup.length == 2 && valueDup.length == 1){
				
				worstCardsInOrder = this.getCardsWithMinimalValue(cards, 4);
				bestCardsInOrder = this.getReversedCardsOrder(worstCardsInOrder);
				
				result = this.valuateEachCardInSet(new String[]{bestCardsInOrder[0],bestCardsInOrder[1]});
			}
			
			//two double color duplicate and two double value duplicate
			// result = two best cards from all; if 1st best == 2nd best(value dup) then
			// result = 1st and 3rd best card from all
			else if( colorDup.length == 2 && valueDup.length == 2){
				
				worstCardsInOrder = this.getCardsWithMinimalValue(cards, 4);
				bestCardsInOrder = this.getReversedCardsOrder(worstCardsInOrder);
				
				if ( this.getCardValue(bestCardsInOrder[0]) == this.getCardValue(bestCardsInOrder[1]) ){

					result = this.valuateEachCardInSet(new String[]{bestCardsInOrder[0],bestCardsInOrder[2]});
				}
				else{
					result = this.valuateEachCardInSet(new String[]{bestCardsInOrder[0],bestCardsInOrder[1]});
				}					
			}
		}
		
		return result;
		
	}
	
	protected int getCardValue(String card){
		int value = -1;
		
		while( card.charAt(0) != this.cardValues[++value]);
		
		return value;
	}

	protected int[] valuateEachCardInSet(String[] cards){
		
		int[] cardsValues = new int[cards.length];
		
		for(int i = 0; i < cards.length; i++){
			cardsValues[i] = this.getCardValue(cards[i]);
		}
		
		return cardsValues;
	}

	protected int getCardSetValue(String[] cards){
		
		int cardSetValue = 0;
		
		int[] eachCardValue = this.valuateEachCardInSet(cards);
		
		for(int i = 0; i < eachCardValue.length; i++){
			cardSetValue += eachCardValue[i];
		}
		
		return cardSetValue;
	}

	protected String getCardWithMinimalValue(String[] cards){
		
		String worstCard = "";
		
		worstCard = this.getCardsWithMinimalValue(cards, 1)[0];
		
		return worstCard;
	}
	
	protected String[] getCardsWithMinimalValue(String[] cards, int amount){
		
		String[] worstCards = new String[amount];
		
		List<String> cardList = new ArrayList<String>(Arrays.asList(cards));
		
		int[] cardValues = this.valuateEachCardInSet(cards);
		
		int[] sortedCardValues = cardValues.clone();
		Arrays.sort(sortedCardValues);
		
		for( int i = 0; i < amount; i++){
			for(String card : cardList){
				if( this.getCardValue(card) == sortedCardValues[i] ){
					worstCards[i] = card;
					cardList.remove(card);
					break;
				}
			}
			
		}
		
		return worstCards;
	}
	
	protected String[] getReversedCardsOrder(String[] cards){
		
		String[] bestCards = new String[cards.length];
		
		for(int i = 0; i < bestCards.length; i++){
			bestCards[i] = cards[bestCards.length-1-i];
		}
		
		return bestCards;
	}
	
	// String[][] we can have triple double {1,2,3} or 2xdouble {{1,2},{3,4}}
	protected int getValueOfNumberOfMinimalCards(String[][] cards, int amount){
		
		int minimalCardsValue = 0;
		
		int[] cardValues;
		
		if( amount > cards[0].length){
			System.out.println("[ERROR]getValueOfNumberOfMinimalCards: amount bigger than amount of cards!");
		}
		else{
			
			for(int j = 0; j < cards.length; j++){
				
				cardValues = this.valuateEachCardInSet(cards[j]);
			
				Arrays.sort(cardValues);
				
				for(int i = 0; i < amount; i++){
					minimalCardsValue += cardValues[i];
				}
			}
		}
		
		return minimalCardsValue;
		
	}
	
	protected int valuateHandFunction(int[] cardValues){
		
		int value = 0;
		
		Arrays.sort(cardValues);
		
		for(int i = 0; i < cardValues.length; i++){
			value += (int)cardValues[i]*Math.pow(10, i);
		}
		
		return value;
	}

	protected String[][] getDuplicateCardByAttribute(String[] cards, String attribute){
		
		String[][] duplicatedCards;
		short attributeIndex;
		
		List<String> cardList = Arrays.asList(cards);
		
		SortedSet<String> firstColorDuplicate = new TreeSet<String>();
		SortedSet<String> secondColorDuplicate = new TreeSet<String>();
		
		Iterator<String> firstIterator;
		Iterator<String> secondIterator;
		
		if (attribute == this.ATTRIBUTE_COLOR){
			attributeIndex = 1;
		}
		else if(attribute == this.ATTRIBUTE_VALUE){
			attributeIndex = 0;
		}
		else{
			System.out.println("Wrong attribute in duplicate getDuplicateCardAttribute");
			
			return new String[][]{};
		}
		
		for (int i = 0; i < cardList.size(); i++){
			for (int j = i+1; j < cardList.size(); j++){	
				if (cardList.get(i).charAt(attributeIndex) == cardList.get(j).charAt(attributeIndex)){
					if(firstColorDuplicate.size() == 0 ||
						cardList.get(i).charAt(attributeIndex) == firstColorDuplicate.first().charAt(attributeIndex)){
						firstColorDuplicate.add(cardList.get(i));
						firstColorDuplicate.add(cardList.get(j));
					}
					else{
						secondColorDuplicate.add(cardList.get(i));
						secondColorDuplicate.add(cardList.get(j));
					}
				}
				
			}
		}
		
		firstIterator = firstColorDuplicate.iterator();
		secondIterator = secondColorDuplicate.iterator();
		
		if (secondColorDuplicate.size() == 0){
			duplicatedCards = new String[1][firstColorDuplicate.size()];
			
			int i = 0;
			while(firstIterator.hasNext()){
				duplicatedCards[0][i++] = firstIterator.next();  
			}
		}
		else{
			//only for 2+2 (two pairs) so both sets have the same size
			duplicatedCards = new String[2][firstColorDuplicate.size()];
			
			int i = 0;
			while(firstIterator.hasNext()){
				duplicatedCards[0][i++] = firstIterator.next();  
			}
			
			i=0;
			
			while(secondIterator.hasNext()){
				duplicatedCards[1][i++] = secondIterator.next(); 
			}
		}
		
		return duplicatedCards;
		
	}

	protected String[][] getDuplicateColorCards(String[] cards){
		return getDuplicateCardByAttribute(cards, this.ATTRIBUTE_COLOR);
	}

	protected String[][] getDuplicateValueCards(String[] cards){
		return getDuplicateCardByAttribute(cards, this.ATTRIBUTE_VALUE);
	}

	protected Set<String> getIntersection(String[] setOne, String[] setTwo){
		
		Set<String> intersectedSet = new TreeSet<String>(Arrays.asList(setOne));
		
		intersectedSet.retainAll(Arrays.asList(setTwo));
		
		return intersectedSet;
	}

	protected Set<String> getDifference(String[] setOne, String[] setTwo){
		
		Set<String> oneDifferenceTwoSet = new TreeSet<String>(Arrays.asList(setOne));
		Set<String> twoDiffecenceOneSet = new TreeSet<String>(Arrays.asList(setTwo));
		Set<String> differenceSet = new TreeSet<String>();
		
		
		oneDifferenceTwoSet.removeAll(Arrays.asList(setTwo));
		twoDiffecenceOneSet.removeAll(Arrays.asList(setOne));
		
		differenceSet.addAll(oneDifferenceTwoSet);
		differenceSet.addAll(twoDiffecenceOneSet);
		
		return differenceSet;
	}
}
